<?php 
// File -> app/controllers/components/swift_mailer.php

/** 
 * SwiftMailer Component based on 4.05 version,
 * this component is inspired by Matt Hugins the developer of
 * SwiftMailer v.3 component based on 3.xx version.
 * 
 * @author Gediminas Morkevicius
 * @version 2.30
 * @license MIT
 * @category Components
 */

//required third party library "SwiftMailer" under GPL license 
App::import('Vendor', 'Swift', array('file' => 'swift_mailer'.DS.'swift_required.php'));

class SwiftMailerComponent extends Object {
	/**
	 * Reference to controller
	 * 
	 * @var Object
	 * @access Private
	 */	
	var $__controller = null;
	/**
	 * List of plugins to load then sending email
	 * 
	 * @var Array - list of plugins in pairs $pluginName/array($arg[0], $arg[...)
	 * @access Private
	 */	
	var $__plugins = array();
	/**
	 * Email layout
	 * 
	 * @var String
	 * @access Public
	 */	
	var $layout = 'default';
	/**
	 * Path to the email template
	 * 
	 * @var String
	 * @access Public
	 */	
	var $viewPath = 'email';
	/**
	 * Send message as type:
	 * 		"html" - content type "html/text"
	 * 		"text" - content type "text/plain"
	 * 		"both" - both content types are included 
	 * 
	 * @var String
	 * @access Public
	 */	
	var $sendAs = 'both';
	/**
	 * Charset for message body
	 * 
	 * @var String
	 * @access Public
	 */	
	var $bodyCharset = 'utf-8';
	/**
	 * Charset for message subject
	 * 
	 * @var String
	 * @access Public
	 */	
	var $subjectCharset = 'utf-8';
	/**
	 * SMTP Security type: 
	 * 		"ssl" - security type
	 * 		"tls" - security type
	 * 
	 * @var String
	 * @access Public
	 */	
	var $smtpType = null;
	/**
	 * SMTP Username for connection
	 * 
	 * @var String
	 * @access Public
	 */	 
	var $smtpUsername = '';
	/**
	 * SMTP Password for connection
	 * 
	 * @var String
	 * @access Public
	 */	 
	var $smtpPassword = '';
	/**
	 * SMTP Host name connection
	 * 
	 * @var String
	 * @access Public
	 */	 
	var $smtpHost = '';
	/**
	 * SMTP port (e.g.: 25 for open, 465 for ssl, etc.)
	 * 
	 * @var Integer
	 * @access Public
	 */	 
	var $smtpPort = 25;
	/**
	 * Seconds before timeout occurs
	 * 
	 * @var Integer
	 * @access Public
	 */	 
	var $smtpTimeout = 10;
	/**
	 * Sendmail command (e.g.: '/usr/sbin/sendmail -bs')
	 * 
	 * @var String
	 * @access Public
	 */	 
	var $sendmailCmd = null;
	/**
	 * Email from address
	 * 
	 * @var String
	 * @access Public
	 */	 
	var $from = null;
	/**
	 * Email from name
	 * 
	 * @var String
	 * @access Public
	 */	 
	var $fromName = null;
	/**
	 * Recipients
	 * 
	 * @var Mixed
	 * 		Array - address/name pairs (e.g.: array(example@address.com => name, ...)
	 * 		String - address to send email to
	 * @access Public
	 */	 
	var $to = null;
	/**
	 * CC recipients
	 * 
	 * @var Mixed
	 * 		Array - address/name pairs (e.g.: array(example@address.com => name, ...)
	 * 		String - address to send email to
	 * @access Public
	 */	 
	var $cc = null;
	/**
	 * BCC recipients
	 * 
	 * @var Mixed
	 * 		Array - address/name pairs (e.g.: array(example@address.com => name, ...)
	 * 		String - address to send email to
	 * @access Public
	 */	 
	var $bcc = null;
	/**
	 * List of files that should be attached to the email.
	 *
	 * @var array - list of file paths
	 * @access public
	 */
	var $attachments = array();
	/**
	 * When the email is opened, if the mail client supports it 
	 * a notification will be sent to this address
	 * 
	 * @var String - email address for notification
	 * @access Public
	 */	 
	var $readNotifyReceipt = null;
	/** 
     * Reply to address
     * 
     * @var Mixed
	 * 		Array - address/name pairs (e.g.: array(example@address.com => name, ...)
	 * 		String - address to send reply to
	 * @access Public
     */
    var $replyTo = null; 
	/**
	 * Max length of email line
	 * 
	 * @var Integer - length of line
	 * @access Public
	 */
		 
	var $maxLineLength = 78;
	/**
	 * Array of errors refreshed after send function is executed
	 * 
	 * @var Array - Error container
	 * @access Public
	 */
	var $postErrors = array();
	
	/**
	 * Initialize component
	 * 
	 * @param Object $controller reference to controller
	 * @access Public
	 */
	function initialize(&$controller) {
		$this->__controller = $controller;
	}
	
	/**
	 * Retrieves html/text or plain/text content from /app/views/elements/$this->viewPath/$type/$template.ctp
	 * and wraps it in layout /app/views/layouts/$this->viewPath/$type/$this->layout.ctp
	 * 
	 * @param String $template - name of the template for content
	 * @param String $type - content type:
	 * 		html - html/text
	 * 		text - plain/text
	 * @return String content from template wraped in layout
	 * @access Protected
	 */
	function _emailBodyPart($template, $type = 'html') {
		$viewClass = $this->__controller->view;

		if ($viewClass != 'View') {
			if (strpos($viewClass, '.') !== false) {
				list($plugin, $viewClass) = explode('.', $viewClass);
			}
			$viewClass = $viewClass . 'View';
			App::import('View', $this->__controller->view);
		}
		$View = new $viewClass($this->__controller, false);
		$View->layout = $this->layout;
		
		$content = $View->element($this->viewPath.DS.$type.DS.$template, array('content' => ""), true);
		$View->layoutPath = $this->viewPath.DS.$type;
		$content = $View->renderLayout($content);
		
		// Run content check callback
		$this->__runCallback($content, 'checkContent');
		
		return $content;
	}
	
	/**
	 * Sends Email depending on parameters specified, using method $method,
	 * mail template $view and subject $subject
	 * 
	 * @param String $view - template for mail content
	 * @param String $subject - email message subject
	 * @param String $method - email message sending method, possible values are:
	 * 		"smtp" - Simple Mail Transfer Protocol method
	 * 		"sendmail" - Sendmail method http://www.sendmail.org/
	 * 		"native" - Native PHP mail method
	 * @return Integer - number of emails sent
	 * @access Public
	 */
	function send($view = 'default', $subject = '', $method = 'smtp') {
		// Check subject charset, asuming we are by default using "utf-8"
		if (strtolower($this->subjectCharset) != 'utf-8') {
			if (function_exists('mb_convert_encoding')) {
				//outlook uses subject in diferent encoding, this is the case to change it
				$subject = mb_convert_encoding($subject, $this->subjectCharset, 'utf-8');
			}
		}
		// Check if swift mailer is imported
		if (!class_exists('Swift_Message')) {
			throw new Exception('SwiftMailer was not included, check the path and filename');
		}
		
		// Create message
		$message = Swift_Message::newInstance($subject);
		
		// Run Init Callback
		$this->__runCallback($message, 'initializeMessage');
		
		$message->setCharset($this->subjectCharset);
		
		// Add html text
		if ($this->sendAs == 'both' || $this->sendAs == 'html') {
			$html_part = $this->_emailBodyPart($view, 'html');
			$message->addPart($html_part, 'text/html', $this->bodyCharset);
			unset($html_part);
		}
		
		// Add plain text or an alternative
		if ($this->sendAs == 'both' || $this->sendAs == 'text') {
			$text_part = $this->_emailBodyPart($view, 'text');
			$message->addPart($text_part, 'text/plain', $this->bodyCharset);
			unset($text_part);
		}
		
		// Add attachments if any
		if (!empty($this->attachments)) {
			foreach($this->attachments as $attachment) {
				if (!file_exists($attachment)) {
					continue;
				}
				$message->attach(Swift_Attachment::fromPath($attachment));
			}
		}
		
		// On read notification if supported
		if (!empty($this->readNotifyReceipt)) {
			$message->setReadReceiptTo($this->readNotifyReceipt);
		}
		
		$message->setMaxLineLength($this->maxLineLength);
		
		// Set the FROM address/name.
		$message->setFrom($this->from, $this->fromName);
		// Add all TO recipients.
		if (!empty($this->to)) {
			if (is_array($this->to)) {
				foreach($this->to as $address => $name) {
					$message->addTo($address, $name);
				}
			} 
			else {
				$message->addTo($this->to);
			}
		}
		
		// Add all CC recipients.
		if (!empty($this->cc)) {
			if (is_array($this->cc)) {
				foreach($this->cc as $address => $name) {
					$message->addCc($address, $name);
				}
			} 
			else {
				$message->addCc($this->cc);
			}
		}
		
		// Add all BCC recipients.
		if (!empty($this->bcc)) {
			if (is_array($this->bcc)) {
				foreach($this->bcc as $address => $name) {
					$message->addBcc($address, $name);
				}
			} 
			else {
				$message->addBcc($this->bcc);
			}
		}

		// Set REPLY TO addresses
        if (!empty($this->replyTo)) {
        	if (is_array($this->replyTo)) {
				foreach($this->replyTo as $address => $name) {
					$message->addReplyTo($address, $name);
				}
			} 
			else {
				$message->addReplyTo($this->replyTo);
			}
        } 
		
		// Initializing mail method object with sending parameters
		$transport = null;
		switch ($method) {
			case 'smtp':
				$transport = Swift_SmtpTransport::newInstance($this->smtpHost, $this->smtpPort, $this->smtpType);
				$transport->setTimeout($this->smtpTimeout);
				if (!empty($this->smtpUsername)) {
					$transport->setUsername($this->smtpUsername);
					$transport->setPassword($this->smtpPassword);
				}
				break;
			case 'sendmail':
				$transport = Swift_SendmailTransport::newInstance($this->sendmailCmd);
				break;
			case 'native': default:
				$transport = Swift_MailTransport::newInstance();
				break;
		}
		
		// Initialize Mailer
		$mailer = Swift_Mailer::newInstance($transport);
		
		// Load plugins if any
		if (!empty($this->__plugins)) {
			foreach($this->__plugins as $name => $args) {
				$plugin_class = "Swift_Plugins_{$name}";
				if (!class_exists($plugin_class)) {
					throw new Exception("SwiftMailer library does not support this plugin: {$plugin_class}");
				}
				
				$plugin = null;
				switch(count($args)) {
					case 1:
						$plugin = new $plugin_class($args[0]);
						break;
					case 2:
						$plugin = new $plugin_class($args[0], $args[1]);
						break;
					case 3:
						$plugin = new $plugin_class($args[0], $args[1], $args[2]);
						break;
					case 4:
						$plugin = new $plugin_class($args[0], $args[1], $args[2], $args[3]);
						break;
					default:
						throw new Exception('SwiftMailer component plugin can register maximum of 4 arguments');
				}
				$mailer->registerPlugin($plugin);
			}
		}
		// Run Send Callback
		$this->__runCallback($message, 'beforeSend');
		
		// Attempt to send the email.
		return $mailer->send($message, $this->postErrors);
	}
	
	/**
	 * Registers a plugin supported by SwiftMailer
	 * function parameters are limited to 5
	 * first argument is plugin name (e.g.: if SwiftMailer plugin class is named "Swift_Plugins_AntiFloodPlugin",
	 * so you should pass name like "AntiFloodPlugin")
	 * All other Mixed arguments included in plugin creation call
	 * 
	 * @return Integer 1 on success 0 on failure
	 */
	function registerPlugin() {
		if (func_num_args()) {
			$args = func_get_args();
			$this->__plugins[array_shift($args)] = $args;
			return true;
		}
		return false;
	}
	
	/**
	 * Run a specific by $type callback on controller
	 * who`s action is being executed. This functionality
	 * is used to perform additional specific methods
	 * if any is required
	 * 
	 * @param mixed $object - object callback being executed on
	 * @param string $type - type of callback to run
	 * @return void
	 */
	function __runCallback(&$object, $type) {
		$call = '__'.$type.'On'.Inflector::camelize($this->__controller->action);
		if (method_exists($this->__controller, $call)) {
			$this->__controller->{$call}($object);
		}
	}
}
?>